/* NoX (NoC Simulator)
 *
 * Dept. of Computer Science & Engineering, Pennsylvania State University.
 * All Rights Reserved.
 *  
 * 1. License     
 * NoX is distributed free of charge for academic, educational, noncommercial 
 * research purposes as long as this notice in its entirety is preserved in
 * every file included in this package.
 * All commercial use of this program requires separate licence. Contact the
 * author for details.
 * 
 * 2. All the publications that used the simulation results generated by the 
 * NoX should notify the author of the publication information and put 
 * following reference.
 *
 *  http://www.cse.psu.edu/~dpark/nox/
 * 
 * 3. Modification of the source code is permitted and encouraged as long as 
 * it follows the terms described in this copyright notice.
 *
 * 4. The author is not responsible for any problems caused by possible errors
 * of the NoX package. Therefore, users should verify the simulation result
 * before using it in their publication.
 *
 * Dept. of Computer Science & Engineering, Pennsylvania State University.
 * Contact: dpark@cse.psu.edu 
 * 
 * 6. If problems are found with the NoX package, please send an email to the
 * author for discussion and correction.

 */

/* Update History
 *
 * Jan. 31, 2006  Version 1.0 released by Dongkook Park 
 *
 */

/* NODE_INIT.C - Initializes node neighbor */

#include <math.h>
#include "main.h"
#include "router.h"
#include "shared.h"

void node_init()
{
  int row, col;
  int current_node;
  int ln, rn, un, dn;
  int i,node = 0;
  // Following variables are used to simplify the code below.
  int nc = MESH_NUM_COLS, nr = MESH_NUM_ROWS; // # of columns and rows
  int d, m;
  int num_nodes = NUM_NODES, num_local_routers, num_global_routers;

  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  //Hybrid topology support
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  if(hybrid_topology == YES)
    num_local_routers = NUM_PE/concentration_degree;
  else
    num_local_routers = 0;
   
  num_global_routers = NUM_NODES - num_local_routers;

  
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  //1. Meshes and Toroids
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  if(topology == MESH || topology == TORUS)
  {
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    //Make node x pc ---> next node connections
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    for(row=0; row<nr; row++)
    {
      for(col=0; col<nc; col++) 
      {
        current_node = num_local_routers + row * nc + col;

        if(topology == MESH)
        {
          dn = (row == 0)?    EDGE : (row-1)*nc+col;
          un = (row == nr-1)? EDGE : (row+1)*nc+col;
          ln = (col == 0)?    EDGE : (row*nc)+col-1;
          rn = (col == nc-1)? EDGE : (row*nc)+col+1;
        }
        else if(topology == TORUS)
        {
          dn = (row-1>=0)?  ((row-1)%nr)*nc+col : ((row+(nr-1))%nr)*nc+col;
          un = ((row+1)%nr)*nc+col;
          ln = (col-1>=0)?  (row*nc)+(col-1)%nc : (row*nc)+(col+(nc-1))%nc;
          rn = (row*nc)+(col+1)%nc;
        }

        neighbor[current_node][LEFT]  = ln == EDGE ? EDGE : ln + num_local_routers;
        neighbor[current_node][RIGHT] = rn == EDGE ? EDGE : rn + num_local_routers;
        neighbor[current_node][UP]    = un == EDGE ? EDGE : un + num_local_routers;
        neighbor[current_node][DOWN]  = dn == EDGE ? EDGE : dn + num_local_routers;
        if(verbose == YES)
        {
          printf("neighbor node:%d pc:%d next_node:%d\n",current_node,LEFT,neighbor[current_node][LEFT]);
          printf("neighbor node:%d pc:%d next_node:%d\n",current_node,RIGHT,neighbor[current_node][RIGHT]);
          printf("neighbor node:%d pc:%d next_node:%d\n",current_node,UP,neighbor[current_node][UP]);
          printf("neighbor node:%d pc:%d next_node:%d\n",current_node,DOWN,neighbor[current_node][DOWN]);
        }
      }// for col
    }// for row
   
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    //update neighbor_pc
    //Make node x pc ---> next pc connections
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    for(int node=num_local_routers; node < NUM_NODES; node++)
    {
      NUM_PC = router_info[node].num_pc;
      NUM_NIC_PC = router_info[node].num_nic_pc;
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //Hybrid topology support
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      if(router_info[node].type == GLOBAL)
        NUM_NIC_PC = 1;

      for(int pc=0; pc < NUM_PC - NUM_NIC_PC; pc++)
      {
        neighbor_pc[node][pc] = (pc + (NUM_PC - NUM_NIC_PC)/2) %(NUM_PC - NUM_NIC_PC);
        if(verbose == YES)
          printf("neighbor_pc node:%d pc:%d next_pc:%d\n",node,pc,neighbor_pc[node][pc]);
      }
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      //Hybrid topology support
      /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
      if(router_info[node].type == GLOBAL)
      {
        neighbor_pc[node][NUM_PC-1] = 1;
        neighbor[node][NUM_PC-1] = node-num_local_routers;
        if(verbose == YES)
        { printf("neighbor_pc node:%d pc:%d next_pc:%d\n",node,NUM_PC-1,neighbor_pc[node][NUM_PC-1]);
          printf("neighbor node:%d pc:%d next_node:%d\n",node,NUM_PC-1,neighbor[node][NUM_PC-1]);
        }
      }
    }

  }


  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  //2. Hybrid topology local routers
  // pc : 0 -- out going pc connection local to global router
  // pc : 1 -- in comomg pc connection global to local router
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  if(hybrid_topology == YES)
  {
    for(int node=0; node < num_local_routers; node++)
    {
      NUM_PC = router_info[node].num_pc;
      NUM_NIC_PC = router_info[node].num_nic_pc;
       if(router_info[node].type == LOCAL)
      {
        if(topology == MESH || topology == TORUS)
        {
          neighbor_pc[node][0] = router_info[num_local_routers].num_pc-1;
          neighbor[node][0] = node+num_local_routers;
        }
        if(topology == FTREE)
        {
          //Assuming same concentration_degree for local and global network
          neighbor_pc[node][0] = node % concentration_degree + 1;
          neighbor[node][0]    = num_local_routers + node/concentration_degree;
        }

        neighbor_pc[node][1] = EDGE_UNIDIRECTION;
        neighbor[node][1] = EDGE_UNIDIRECTION;
        if(verbose == YES)
        {
          int pc = 0;
          printf("neighbor (%d : %d) ---> (%d : %d)\n",node,pc,neighbor[node][pc],neighbor_pc[node][pc]);
          pc= 1;
          printf("neighbor (%d : %d) ---> (%d : %d)\n",node,pc,neighbor[node][pc],neighbor_pc[node][pc]);
        }
      }

    }
   
  }
  
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  //3. Butterfly
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  if(topology == BFLY)
  {
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    //Make node x pc ---> next node connections
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    int dimconst=0;
    for(int node=0; node < (int)pow(bfly.k,bfly.n-1); node++)
      for(int d=0; d< bfly.n - 1 ; d++)
      {
        dimconst = (int)(floor(node/pow(bfly.k, d))) % bfly.k; 
        for(int m=0,port=0; m< bfly.k ; m++)
        {
          int tmp;
          tmp = node + (m - dimconst)*(int)pow(bfly.k,d);
          if(tmp != node)
          {
            neighbor[node][port + d*(bfly.k-1)] = tmp; 
            if(verbose == YES)
              printf("node : pc : d : neighbor %d :%d :%d :%d\n",node, port + d*(bfly.k-1), d,tmp); 
            port++;
          }
        }
      }

    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    //update neighbor_pc
    //Make node x pc ---> next pc connections
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
    for(int node=0; node < NUM_NODES; node++)
    {
      NUM_PC = router_info[node].num_pc;
      NUM_NIC_PC = router_info[node].num_nic_pc;

      for(int pc=0; pc < NUM_PC-NUM_NIC_PC; pc++)
      {
        int dnode=neighbor[node][pc];
        for(int npc=0; npc < NUM_PC-NUM_NIC_PC; npc++)
          if(neighbor[dnode][npc] == node)
          {
            neighbor_pc[node][pc]=npc;
            break;
          }
        if(verbose == YES)
          printf("neighbor (%d : %d) ---> (%d : %d)\n",node,pc,neighbor[node][pc],neighbor_pc[node][pc]);
      }
    }
  }
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  //4. Fat Tree
  /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
  //The root node is connected to multiple high radix routers
  //each leaf high radix router has only 1 routing pc and around 4-8 nic pc's
  //this routing pc is connected to root node
  if(topology == FTREE)
  {
    for(int node=num_local_routers; node < NUM_NODES; node++)
    {
      NUM_PC = router_info[node].num_pc;
      NUM_NIC_PC = router_info[node].num_nic_pc;
      for(int pc=0; pc < NUM_PC-NUM_NIC_PC; pc++)
      {
        if(router_info[node].type == FLAT)
        {
          neighbor[node][pc] = NUM_NODES-1;
          neighbor_pc[node][pc] = node-num_local_routers;
          if(node == NUM_NODES - 1)
          {
            //each pc of the root node is connected to leaf high radix router
            neighbor[node][pc] = num_local_routers + pc;
            neighbor_pc[node][pc] = 0;
          }
        }
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
        //Hybrid topology support
        /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
        //make global to root node connections
        if(router_info[node].type == GLOBAL && pc < 1)
        {
          neighbor[node][pc] = NUM_NODES-1;
          neighbor_pc[node][pc] = node-num_local_routers;
        }
        //make global to local connections
        if(router_info[node].type == GLOBAL && pc >= 1)
        {
          neighbor[node][pc] = (node-num_local_routers)*concentration_degree + pc - 1;
          neighbor_pc[node][pc] = 1;
        }
        //make root node to other global node connections
       if(router_info[node].type == GLOBAL && node == NUM_NODES - 1 && num_global_routers != 1)
        {
          neighbor[node][pc] = num_local_routers + pc;
          neighbor_pc[node][pc] = 0;
        }
        if(verbose == YES)
          printf("neighbor (%d : %d) ---> (%d : %d)\n",node,pc,neighbor[node][pc],neighbor_pc[node][pc]);
      }//for pc
    }//for node
   }// Fat Tree

}
